package vip.liux.backend.infrastructure.jpa.files;

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.jetbrains.annotations.Nullable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import vip.liux.contracts.models.files.DirectoryDescriptor;
import vip.liux.contracts.repositories.files.DirectoryDescriptorRepository;

import java.util.ArrayList;
import java.util.List;

/**
 * DirectoryDescriptorJpaRepository 是一个 JPA 仓库接口，
 * 用于操作 DirectoryDescriptor 实体。
 * 它实现了 JpaSpecificationExecutor 以支持动态查询。
 */
public interface DirectoryDescriptorJpaRepository extends JpaRepository<DirectoryDescriptor, Long>, DirectoryDescriptorRepository, JpaSpecificationExecutor<DirectoryDescriptor> {

    /**
     * 构建 parentId 的查询条件谓词。
     * 如果 parentId 为 null，则返回 parent 字段为 null 的条件。
     * 如果 parentId 不为 null，则返回 parent.id 等于 parentId 的条件。
     *
     * @param c        CriteriaBuilder 用于构建查询条件
     * @param r        查询的根实体
     * @param parentId 父目录 ID
     * @return 构建的谓词
     */
    private Predicate buildParentPredicate(CriteriaBuilder c, Root<DirectoryDescriptor> r, @Nullable Long parentId) {
        if (parentId == null) {
            return c.isNull(r.get("parent"));
        } else {
            return c.equal(r.get("parent").get("id"), parentId);
        }
    }

    /**
     * 根据名称和父目录 ID 查找目录。
     *
     * @param name     目录名称
     * @param parentId 父目录 ID
     * @return 匹配的 DirectoryDescriptor 实体
     */
    @Override
    default DirectoryDescriptor findByName(String name, @Nullable Long parentId) {
        return findOne((r, q, c) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(c.equal(r.get("name"), name));
            predicates.add(buildParentPredicate(c, r, parentId));
            return c.and(predicates.toArray(new Predicate[0]));
        }).orElse(null);
    }

    /**
     * 获取指定父目录下的子目录数量。
     *
     * @param parentId 父目录 ID
     * @return 子目录数量
     */
    @Override
    default int getChildrenCount(@Nullable Long parentId) {
        return (int) count((r, q, c) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(buildParentPredicate(c, r, parentId));
            return c.and(predicates.toArray(new Predicate[0]));
        });
    }

    @Override
    default long findByParent(@Nullable Long parentId) {
        return count((r, q, c) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(buildParentPredicate(c, r, parentId));
            q.select(r.get("id"));
            return c.and(predicates.toArray(new Predicate[0]));
        });
    }

    /**
     * 获取指定父目录下的子目录列表，支持过滤和排序。
     *
     * @param parentId 父目录 id
     * @param filter   名称过滤条件
     * @param sort     排序条件
     * @return 子目录列表
     */
    @Override
    default List<DirectoryDescriptor> getChildren(@Nullable Long parentId, String filter, Sort sort) {
        return findAll((r, q, c) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(buildParentPredicate(c, r, parentId));
            if (filter != null) {
                predicates.add(c.like(r.get("name"), "%" + filter + "%"));
            }
            return c.and(predicates.toArray(new Predicate[0]));
        }, sort);
    }

    /**
     * 获取指定目录及其所有子目录的 ID 列表。
     *
     * @param id 目录 ID
     * @return 所有子目录的 ID 列表
     */
    @Override
    default List<Long> getChildren(@Nullable Long id) {
        List<Long> subDirectoryIds = getChildrenIds(id);
        List<Long> allSubDirectoryIds = new ArrayList<>(subDirectoryIds);
        for (Long subDirectoryId : subDirectoryIds) {
            allSubDirectoryIds.addAll(getChildrenIds(subDirectoryId));
        }
        return allSubDirectoryIds;
    }

    /**
     * 获取指定目录的直接子目录 ID 列表。
     *
     * @param id 目录 ID
     * @return 子目录 ID 列表
     */
    default List<Long> getChildrenIds(@Nullable Long id) {
        return findAll((r, q, c) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(buildParentPredicate(c, r, id));
            q.select(r.get("id"));
            return c.and(predicates.toArray(new Predicate[0]));
        }).stream().map(DirectoryDescriptor::getId).toList();
    }

    /**
     * 获取指定目录及其所有子目录的实体列表。
     *
     * @param id 目录 ID
     * @return 所有子目录的实体列表
     */
    @Override
    default List<DirectoryDescriptor> getAllChildren(@Nullable Long id) {
        List<DirectoryDescriptor> subDirectories = getChildrenByParentId(id);
        List<DirectoryDescriptor> allSubDirectories = new ArrayList<>(subDirectories);
        for (DirectoryDescriptor subDirectory : subDirectories) {
            allSubDirectories.addAll(getChildrenByParentId(subDirectory.getId()));
        }
        return allSubDirectories;
    }

    /**
     * 获取指定父目录的直接子目录列表。
     *
     * @param parentId 父目录 ID
     * @return 子目录列表
     */
    default List<DirectoryDescriptor> getChildrenByParentId(@Nullable Long parentId) {
        return findAll((r, q, c) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(buildParentPredicate(c, r, parentId));
            return c.and(predicates.toArray(new Predicate[0]));
        });
    }
}